
package com.scs.application.modules.settlement.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.scs.application.consts.SerialRuleConsts;
import com.scs.application.core.model.CommonResult;
import com.scs.application.core.serial.SerialUtils;
import com.scs.application.core.service.BaseServiceImpl;
import com.scs.application.core.utils.BeanUtils;
import com.scs.application.core.utils.SerialOwnerUtils;
import com.scs.application.core.utils.StringUtils;
import com.scs.application.core.utils.UserUtils;
import com.scs.application.modules.base.request.HospitalSupplierRequest;
import com.scs.application.modules.base.service.HospitalSupplierService;
import com.scs.application.modules.base.vo.HospitalSupplierVO;
import com.scs.application.modules.settlement.consts.DictConsts;
import com.scs.application.modules.settlement.dto.BillDTO;
import com.scs.application.modules.settlement.dto.BillStatusDTO;
import com.scs.application.modules.settlement.entity.Bill;
import com.scs.application.modules.settlement.entity.BillItem;
import com.scs.application.modules.settlement.entity.Invoice;
import com.scs.application.modules.settlement.mapper.BillMapper;
import com.scs.application.modules.settlement.request.BillQueryRequest;
import com.scs.application.modules.settlement.service.BillItemService;
import com.scs.application.modules.settlement.service.BillService;
import com.scs.application.modules.thirdparty.erp.request.BillConfirmRequest;
import com.scs.application.modules.thirdparty.erp.request.BillRequest;
import com.scs.application.modules.thirdparty.erp.request.ConfirmRequest;
import com.scs.application.modules.thirdparty.erp.request.InvoiceRequest;
import com.scs.application.modules.thirdparty.erp.vo.BillVO;
import com.scs.application.modules.thirdparty.erp.vo.SettlementVO;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 记账单 service 实现
 */
@Slf4j
@AllArgsConstructor
@Service
@Transactional(rollbackFor = Exception.class)
public class BillServiceImpl extends BaseServiceImpl<BillMapper, Bill> implements BillService {

    private final BillItemService billItemService;

    private final HospitalSupplierService hospitalSupplierService;

    @Override
    public boolean removeCascadeById(Serializable id) {
        billItemService.remove(Wrappers.<BillItem>query().eq("bill_id", id));
        return super.removeById(id);
    }

    @Override
    public List<Bill> list(BillQueryRequest request) {
        Date beginGmtCreate = request.getBeginGmtCreate();
        List<Bill> list = this.list(Wrappers.<Bill>query()
                .eq(request.getStatus() != null, "status", request.getStatus())
                .between(beginGmtCreate != null && request.getEndGmtCreate() != null, "gmt_create", beginGmtCreate, request.getEndGmtCreate())
                .orderByDesc("code")
        );
        return list;
    }

    @Override
    public IPage<Bill> page(BillQueryRequest request) {
        return this.page(request.getPage(), getQueryWrapper(request));
    }

    private QueryWrapper<Bill> getQueryWrapper(BillQueryRequest request) {
        QueryWrapper<Bill> wrapper = Wrappers.<Bill>query()
                .like(StringUtils.isNotBlank(request.getCode()), "code", request.getCode())
                .like(StringUtils.isNotBlank(request.getSupplierName()), "supplier_name", request.getSupplierName())
                .like(StringUtils.isNotBlank(request.getDeptName()), "dept_name", request.getDeptName())
                .eq(request.getStatus() != null, "status", request.getStatus())
                .between(request.getBeginGmtCreate() != null && request.getEndGmtCreate() != null, "gmt_create", request.getBeginGmtCreate(), request.getEndGmtCreate())
                .orderByDesc("code");
        if (UserUtils.isHospital()) {
            wrapper.eq("hospital_id", UserUtils.getHospitalId());
        }
        if (UserUtils.isSupplier()) {
            wrapper.eq("supplier_id", UserUtils.getSupplierId());
        }
        return wrapper;
    }

    @Override
    public boolean add(Bill entity) {
        //默认值
        entity.setHospitalId(UserUtils.getHospitalId())
                .setHospitalName(UserUtils.getHospital().getName())
                .setCode(SerialUtils.generateCode(SerialRuleConsts.SETTLEMENT_BILL_CODE, SerialOwnerUtils.getHospitalOwner(entity.getHospitalId())))
                .setTotalAmount(0.0)
        ;
        return saveOrUpdate(entity);
    }


    /**
     * 第三方记账单接口批量保存记账单
     */

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void batchSaveForDTO(List<BillDTO> billDTOS) {
        String hospitalId = UserUtils.getHospitalId();
//        String hospitalId = "1244433999238021122";
        // 不存在重复的记账单数据
        List<BillDTO> dtos = Lists.newArrayListWithCapacity(billDTOS.size());
        List<String> billCodes = billDTOS.parallelStream().map(BillDTO::getCode).distinct().collect(Collectors.toList());
        List<Bill> existsBills = this.list(Wrappers.<Bill>query().select("code").in("code", billCodes).eq("hospital_id", hospitalId));
        HospitalSupplierRequest hospitalSupplierRequest = new HospitalSupplierRequest();
        hospitalSupplierRequest.setHospitalId(hospitalId);
        List<HospitalSupplierVO> hospitalSupplierVOS = hospitalSupplierService.listVO(hospitalSupplierRequest);
        if (hospitalSupplierVOS == null || hospitalSupplierVOS.isEmpty()) {
            log.warn("该医院没有任何有效供应商，医院ID为{}", hospitalId);
            return;
        }
        for (BillDTO billDTO : billDTOS) {
            // 若该条记账单编号数据库已存在
            String code = billDTO.getCode();
            if (existsBills != null && existsBills.size() > 0) {
                Optional<Bill> optionalBill = existsBills.stream().filter(bill -> code.equalsIgnoreCase(bill.getCode())).findFirst();
                if (optionalBill.isPresent()) {
                    log.info("记账单号 {} 已经存在，不能重复添加", code);
                    continue;
                }
                // throw new BusinessException(MessageFormat.format("记账单号已经存在，不能重复添加", billDTO.getCode()));
            }
            // 供应商id是否正确
            String supplierId = billDTO.getSupplierId();
            Optional<HospitalSupplierVO> optionalHospitalSupplierVO = hospitalSupplierVOS.stream().filter(hsvo -> supplierId.equalsIgnoreCase(hsvo.getSupplierId())).findFirst();
            if (!optionalHospitalSupplierVO.isPresent()) {
                log.warn("供应商({} - {}) 与 {} 未建立联系，不支持推送", billDTO.getSupplierName(), supplierId, UserUtils.getHospital().getName());
                continue;
                // throw new BusinessException(MessageFormat.format("供应商ID {%s} 不存在，不能添加", supplierId));
            }

            dtos.add(billDTO);
        }
        // 保存操作
        if(dtos.isEmpty()){
            return;
        }
        // 记账单数据
        List<Bill> list = Lists.newArrayListWithCapacity(dtos.size());
        Bill bill;
        // 订单详情数据
        for (BillDTO billDTO : dtos) {
            bill = new Bill();
            BeanUtils.copyProperties(billDTO, bill);

            double totalAmount = billDTO.getItems().stream().mapToDouble(o -> o.getPrice() * o.getQty()).sum();
            bill.setTotalAmount(totalAmount);

            //如果非寄售记账单，添加状态为可结算
            bill.setHospitalId(hospitalId)
                    .setHospitalName(UserUtils.getHospital().getName())
                    // .setStatus(billDTO.getIsConsignment() == GlobalConsts.YES ? DictConsts.BILL_STATUS_SETTLEABLE : DictConsts.BILL_STATUS_INVOICED);
                    .setStatus(DictConsts.BILL_STATUS_SETTLEABLE);
            list.add(bill);
        }

        // 保存记账单主表
        saveBatch(list);

        Map<String, String> map = list.stream().collect(Collectors.toMap(o -> o.getCode(), o -> o.getId(), (o1, o2) -> o1));

        List<BillItem> itemList = new ArrayList<>();
        for (BillDTO billDTO : dtos) {
            List<BillItem> tmpList = Lists.newArrayListWithExpectedSize(billDTO.getItems().size());
            BeanUtils.copyProperties(Lists.newArrayList(billDTO.getItems()), tmpList, BillItem.class);
            //如果记账单为非寄售则添加记账详单状态为未开票
            tmpList.stream().forEach(o -> {
                o.setBillId(map.get(billDTO.getCode()))
                        /*.setStatus(billDTO.getIsConsignment() == GlobalConsts.YES? DictConsts.BILL_ITEM_STATUS_UNINVOICE : DictConsts.BILL_ITEM_STATUS_INVOICED)*/
                        .setSubtotalAmount(o.getPrice() * o.getQty())
                ;

            });
            itemList.addAll(tmpList);
        }

        // 保存记账单明细表
        billItemService.saveBatch(itemList);

    }

    @Override
    public void receiveBills(List<BillDTO> billDTOS) {
        billDTOS.forEach(billDTO -> {
            for (BillDTO.BillItemDTO itemDTO : billDTO.getItems()) {
                if (StringUtils.isBlank(itemDTO.getManufacturerName())) {
                    itemDTO.setManufacturerName("默认厂商");
                }
                if (StringUtils.isBlank(itemDTO.getMaterialSpec())) {
                    itemDTO.setMaterialSpec("默认品规");
                }
            }
            billDTO.setHospitalId(UserUtils.getHospitalId()).setHospitalName(UserUtils.getHospital().getName());
        });
        batchSaveForDTO(billDTOS);
    }

    /**
     * 第三方记账单接口批量修改记账单状态
     */

    @Override
    public void batchUpdateStatusForDTO(List<BillStatusDTO> billStatusDTOS) {

        for (BillStatusDTO dto : billStatusDTOS) {
            String code = dto.getCode();
            Bill bill = this.getOne(Wrappers.<Bill>query()
                    .eq("hospital_id", UserUtils.getHospitalId())
                    .eq("code", code)
                    .last(" limit 1")
            );
            if (bill == null) {
                // throw new BusinessException(MessageFormat.format("记账单不存在", code));
                log.info("记账单 {} 不存在", code);
                continue;
            }
            //如果状态为已结算
            if (bill.getStatus().equals(DictConsts.BILL_STATUS_INVOICED)) {
                log.info("记账单{}已开票，不能修改状态", code);
                continue;
                // throw new BusinessException(MessageFormat.format("记账单{0}已开票，不能修改状态", code));
            }
            this.update(Wrappers.<Bill>update()
                    .eq("code", bill.getCode())
                    .set("status", dto.getStatus())
            );
            //如果记账单状态为可结算 1， 则记账详单状态为未开票 0
            billItemService.update(Wrappers.<BillItem>update()
                    .eq("bill_id", bill.getId())
                    .set("status", dto.getStatus() == DictConsts.BILL_STATUS_SETTLEABLE ? DictConsts.BILL_ITEM_STATUS_UNINVOICE : DictConsts.BILL_ITEM_STATUS_INVOICED));

        }
    }

    /**
     * 第三方记账单接口批量保存记账单
     *
     *  已接收的会跳过
     */

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void batchSaveForFeign(List<BillDTO> billDTOS) {

        String hospitalId = UserUtils.getHospitalId();
//        String hospitalId = "1244433999238021122";
        // 不存在重复的记账单数据
        List<BillDTO> dtos = Lists.newArrayListWithCapacity(billDTOS.size());
        List<String> billCodes = billDTOS.parallelStream().map(BillDTO::getCode).distinct().collect(Collectors.toList());
        List<Bill> existsBills = this.list(Wrappers.<Bill>query().select("code").in("code", billCodes).eq("hospital_id", hospitalId));
        HospitalSupplierRequest hospitalSupplierRequest = new HospitalSupplierRequest();
        hospitalSupplierRequest.setHospitalId(hospitalId);
        List<HospitalSupplierVO> hospitalSupplierVOS = hospitalSupplierService.listVO(hospitalSupplierRequest);
        if (hospitalSupplierVOS == null || hospitalSupplierVOS.isEmpty()) {
            log.warn("该医院没有任何有效供应商，医院ID为{}", hospitalId);
            return;
        }
        for (BillDTO billDTO : billDTOS) {
            // 若该条记账单编号数据库已存在
            String code = billDTO.getCode();
            if (existsBills != null && existsBills.size() > 0) {
                Optional<Bill> optionalBill = existsBills.stream().filter(bill -> code.equalsIgnoreCase(bill.getCode())).findFirst();
                if (optionalBill.isPresent()) {
                    log.info("记账单号 {} 已经存在，不能重复添加", code);
                    continue;
                }
                // throw new BusinessException(MessageFormat.format("记账单号已经存在，不能重复添加", billDTO.getCode()));
            }
            // 供应商id是否正确
            String supplierId = billDTO.getSupplierId();
            Optional<HospitalSupplierVO> optionalHospitalSupplierVO = hospitalSupplierVOS.stream().filter(hsvo -> supplierId.equalsIgnoreCase(hsvo.getSupplierId())).findFirst();
            if (!optionalHospitalSupplierVO.isPresent()) {
                log.warn("供应商({} - {}) 与 {} 未建立联系，不支持推送", billDTO.getSupplierName(), supplierId, UserUtils.getHospital().getName());
                continue;
                // throw new BusinessException(MessageFormat.format("供应商ID {%s} 不存在，不能添加", supplierId));
            }

            dtos.add(billDTO);
        }
        // 保存操作
        if(dtos.isEmpty()){
            return;
        }
        // 记账单数据
        List<Bill> list = Lists.newArrayListWithCapacity(dtos.size());
        Bill bill;
        // 订单详情数据
        for (BillDTO billDTO : dtos) {
            bill = new Bill();
            BeanUtils.copyProperties(billDTO, bill);

            double totalAmount = billDTO.getItems().stream().mapToDouble(o -> o.getPrice() * o.getQty()).sum();
            bill.setTotalAmount(totalAmount);

            //如果非寄售记账单，添加状态为可结算
            bill.setHospitalId(hospitalId)
                    .setHospitalName(UserUtils.getHospital().getName())
                    // .setStatus(billDTO.getIsConsignment() == GlobalConsts.YES ? DictConsts.BILL_STATUS_SETTLEABLE : DictConsts.BILL_STATUS_INVOICED);
                    .setStatus(DictConsts.BILL_STATUS_SETTLEABLE);
            list.add(bill);
        }

        // 保存记账单主表
        saveBatch(list);

        Map<String, String> map = list.stream().collect(Collectors.toMap(o -> o.getCode(), o -> o.getId(), (o1, o2) -> o1));

        List<BillItem> itemList = new ArrayList<>();
        for (BillDTO billDTO : dtos) {
            List<BillItem> tmpList = Lists.newArrayListWithExpectedSize(billDTO.getItems().size());
            BeanUtils.copyProperties(Lists.newArrayList(billDTO.getItems()), tmpList, BillItem.class);
            //如果记账单为非寄售则添加记账详单状态为未开票
            tmpList.stream().forEach(o -> {
                o.setBillId(map.get(billDTO.getCode()))
                        /*.setStatus(billDTO.getIsConsignment() == GlobalConsts.YES? DictConsts.BILL_ITEM_STATUS_UNINVOICE : DictConsts.BILL_ITEM_STATUS_INVOICED)*/
                        .setSubtotalAmount(o.getPrice() * o.getQty())
                ;

            });
            itemList.addAll(tmpList);
        }

        // 保存记账单明细表
        billItemService.saveBatch(itemList);
    }

    @Override
    public List<SettlementVO> getSettlement(String startDate) {
        List<Bill> list = this.list(Wrappers.<Bill>query().eq("status",0).gt(StringUtils.isNotBlank(startDate),"gmt_create", startDate)
                .orderByDesc("code")
        );
        List<SettlementVO> dtoList = Lists.newArrayListWithCapacity(list.size());

        for (int i = 0; i < list.size(); i++) {
            Bill bill = list.get(i);
            SettlementVO vo = new SettlementVO();
            vo.setPeriodNo(bill.getPeriodNo());
            vo.setSettlementCode(bill.getCode());
            vo.setSettlementDate(bill.getGmtSettlement());

            List<BillItem> itemList = billItemService.list(Wrappers.<BillItem>query().eq("bill_id", bill.getId()));
            List<SettlementVO.Item> dtoItemList = Lists.newArrayListWithCapacity(itemList.size());
            for (int j = 0; j < itemList.size() ; j++) {
                BillItem item = itemList.get(j);
                SettlementVO.Item itemVO = new SettlementVO.Item();
                BeanUtils.copyProperties(item, itemVO, SettlementVO.Item.class);
                itemVO.setDeliveryItemId(item.getThirdOrderItemId());
                itemVO.setDeliveryOrderId(item.getSaleLot());
                itemVO.setSettlementItemId(item.getId());
                itemVO.setProductionDate(item.getMfgDate());
                dtoItemList.add(itemVO);
            }
            vo.setItems(dtoItemList);
            dtoList.add(vo);
        }

        return dtoList;
    }

    @Override
    public CommonResult syncSettlementStatus(String settlementCode, Integer settlementStatus) {

        if(settlementStatus == 1){
            Bill bill = new Bill();
            bill.setStatus(1);
            this.update(bill,Wrappers.<Bill>query().eq("code",settlementCode));
        }else{
            CommonResult.error(" settlementStatus 不支持的状态");
        }

        return CommonResult.ok("成功");
    }

    @Override
    public CommonResult billDownloadConfirm(BillConfirmRequest request) {
        final int status = 4;
        List<ConfirmRequest> confirm = request.getConfirm();
        Set<String> codeList = confirm.stream().filter(o -> o.getStatus() == status).map(o -> o.getCode()).collect(Collectors.toSet());
        Map<String, Integer> map = confirm.stream().collect(Collectors.toMap(o -> o.getCode(), o -> o.getStatus(), (k1, k2) -> k1));
        Set<String> reqCode = map.keySet();

        final int len = 500;
        if (confirm == null || confirm.size() > len) {
            return CommonResult.error("确认订单的长度请保持在" + len + "个以内");
        }
        List<Bill> list;
        if(codeList.size()>0){
            // 查出需要更新的bill
            list = this.list(Wrappers.<Bill>query().eq("hospital_id", request.getHospitalId()).in("code", codeList));
        }else{
            list = new ArrayList<>();
        }

        for (Bill b:
                list) {
            if(map.containsKey(b.getCode())){
                b.setStatus(map.get(b.getCode()));
            }
        }
        if(list.size()>0){
            // 执行更新
            this.updateBatchById(list);
            // 移除已经更新的bill
            List<String> collect = list.stream().map(o -> o.getCode()).collect(Collectors.toList());
            reqCode.removeAll(collect);
        }

        if (reqCode.size() > 0) {
            // 返回还没有更新的bill
            return CommonResult.error("失败", reqCode.toString());
        }

        return CommonResult.ok("成功");
    }

    @Override
    public CommonResult invoiceInfoUpload(InvoiceRequest request) {

        String supplierId = "";
        Invoice invoice = new Invoice();
        invoice.setCode(request.getInvoiceCode());
        invoice.setInvoiceNo(request.getInvoiceNo());
        invoice.setGmtMakeOut(request.getInvoiveDate());
        invoice.setTotalAmount(request.getInvoiceAmount());
        invoice.setDrawer(request.getInvoiceUser());
        invoice.setSupplierId(supplierId);
        invoice.setSupplierName(request.getSupplierName());
        invoice.setHospitalId(request.getHospitalId());

        // TODO 明细

        return null;
    }

    @Override
    public CommonResult billDownload(BillRequest request) {
        QueryWrapper<Bill> query = Wrappers.<Bill>query()
                .eq("hospital_id", request.getHospitalId());
        if (Objects.nonNull(request.getBillCode()) && request.getBillCode().size() > 0) {
            if (request.getBillCode().size() > 500){
                return CommonResult.error("OrderCode 超过500长度");
            }
            query.in("code", request.getBillCode());
        } else if (!StringUtils.isAllBlank(request.getStartDate(), request.getEndDate())) {
            query.between("gmt_bill", request.getStartDate(), request.getEndDate());
        } else {
            return CommonResult.error("参数错误");
        }
        List<Bill> list = this.list(query);
        List<BillVO> vos = Lists.newArrayListWithCapacity(list.size());
        for (Bill b:
                list) {
            BillVO vo = new BillVO();
            BeanUtils.copyProperties(b,vo);
            vo.setBillCode(b.getCode());
            vo.setBillDate(b.getGmtBill());
            List<BillItem> itemList = billItemService.list(Wrappers.<BillItem>query().eq("bill_id", b.getId()));
            List<BillVO.Item> voItemList = Lists.newArrayListWithCapacity(itemList.size());
            BeanUtils.copyProperties(itemList, voItemList, BillVO.Item.class);
            for (int i = 0; i < voItemList.size(); i++) {
                BillItem billItem = itemList.get(i);
                BillVO.Item item = voItemList.get(i);
                item.setBillDtlId(billItem.getId());
                item.setProductionDate(billItem.getMfgDate());
                item.setExpiredDate(billItem.getExpDate());
            }
            vo.setItems(voItemList);
            vos.add(vo);
        }
        return CommonResult.ok("成功", vos);
    }
}
